#-*- coding: utf-8 -*-
import os
import os.path
from xml.dom.minidom import Document, Node, Element, parse
import xml.dom.minidom
import base64

import wx

import tex

class ToolsBase (object):
	def __init__ (self, mainwnd, toolspanel):
		self.mainwnd = mainwnd
		self.toolspanel = toolspanel
		self.packages_dir = "tools"
		self.package_ex = ".tools"

	def GetItemsText (self):
		result = []
		count = self.toolspanel.GetPageCount()
		for n in xrange (count):
			page = self.toolspanel.GetPage(n)
			result += page.GetItemsText()

		result.sort()
		return result

					
	def Save (self):
		files = dict()
		
		count = self.toolspanel.GetPageCount()
		for n in xrange (count):
			page = self.toolspanel.GetPage(n)
			
			if page.package_name in files:
				doc, root = files[page.package_name]
			else:					
				doc=Document()
				root = doc.createElement ("root")
				doc.appendChild (root)
				
				files[page.package_name] = (doc, root)
			
			page_xml = self.CreatePageXml (page, doc)
			root.appendChild (page_xml)
		
		for key, (doc, root) in files.iteritems():
			fname = os.path.join (self.packages_dir, key)
			
			fp = file (fname, "w")
			xml_text = doc.toprettyxml ("\t", "\n", encoding = "utf-8")
			fp.write (xml_text)
			fp.close()
		
	
	def Load (self):
		"""Загрузить панели с инструментами из пакетов"""
		files = os.listdir(self.packages_dir)
		for fname in files:
			if fname.endswith (self.package_ex):
				package_path = os.path.join (self.packages_dir, fname)
		
				try:
					doc = parse (package_path)
				except IOError:
					return
				except xml.parsers.expat.ExpatError:
					print "Parse error"
					return
				
				package_name = fname
				
				for root_node in doc.childNodes:
					# Ищем корневой элемент
					if root_node.nodeType  ==  Node.ELEMENT_NODE and root_node.tagName == u"root":
						for page_node in root_node.childNodes:				
							# Перебираем  теги в поисках страниц с элементами
							if page_node.nodeType  ==  Node.ELEMENT_NODE and page_node.tagName == u"page":
								name = page_node.getAttribute (u"name")
								page = self.toolspanel.AddPage (name, package_name)
								
								# Перебираем  теги в поисках элементов
								for item_node in page_node.childNodes:
									if item_node.nodeType  ==  Node.ELEMENT_NODE and item_node.tagName == u"item":
										#print page_node.tagName
										self.ParseItem (item_node, page)

	def ParseItem (self, item_tag, page):
		"""Парсить тег со страницей инструментов"""
		text = None
		pos = None
		image = None
		image_tex = ""
		
		for node in item_tag.childNodes:
			if node.nodeType  ==  Node.ELEMENT_NODE and node.tagName == u"position":
				pos = self.GetIntValue (node)
				#print pos
			elif node.nodeType  ==  Node.ELEMENT_NODE and node.tagName == u"text":
				text = self.GetStringValue (node).strip()
			elif node.nodeType  ==  Node.ELEMENT_NODE and node.tagName == u"image":
				image = self.GetImageValue (node)
			elif node.nodeType  ==  Node.ELEMENT_NODE and node.tagName == u"imagetex":
				image_tex = self.GetStringValue (node).strip()
		
		if text and pos and image:
			#print text
			item = page.AddItem (text, pos, image, image_tex)
			self.mainwnd.Bind (wx.EVT_BUTTON, self.mainwnd.onToolsClick, id = item.id)
	
	def GetIntValue (self, tag):
		for node in tag.childNodes:
			if node.nodeType == Node.TEXT_NODE:
				try:
					result = int (node.data)
					return result
				except TypeError:
					return

	def GetImageValue (self, tag):
		buffer = None
		width = None
		height = None
		
		try:
			width = int (tag.getAttribute (u"width") )
			height = int (tag.getAttribute (u"height") )
		except TypeError:
			return
		
		#print "%d x %d" % (width, height)
		
		for node in tag.childNodes:
			if node.nodeType == Node.CDATA_SECTION_NODE:
				buffer = base64.b64decode (node.data)
		
		if buffer and width and height:
			image = wx.ImageFromBuffer (width, height, buffer)
			image.SetMaskColour (255, 0, 255)
			image.SetMask()
			return image

	def GetStringValue (self, tag):
		for node in tag.childNodes:
			if node.nodeType == Node.TEXT_NODE:
				return node.data

	
	def CreateImageTag (self, doc, image):
		"""Создать тег image с картинкой"""
		image_tag = doc.createElement (u"image")
		
		image_tag.setAttribute ("width", str (image.GetWidth() ) )
		image_tag.setAttribute ("height", str (image.GetHeight() ) )
		
		# Буфер с картикой
		image_text = xml.dom.minidom.CDATASection()		
		
		image_data = str (image.GetDataBuffer() )
		image_data_b64 = base64.b64encode (image_data)
		
		image_text.data = image_data_b64
		image_tag.appendChild (image_text)
		
		return image_tag

	def CreatePageXml (self, page, doc):
		group_tag = doc.createElement (u"page")
		group_tag.setAttribute ("name", page.name)
		
		for item in page.items:
			item_tag = doc.createElement (u"item")
		
			text_tag = doc.createElement (u"text")
			position_tag = doc.createElement (u"position")
			image_tex_tag = doc.createElement (u"imagetex")

			# Сохранить картинку в XML
			image_tag = self.CreateImageTag (doc, item.image)

			# Текст
			text_text = doc.createTextNode (item.text)
			text_tag.appendChild (text_text)

			# Положенеи курсора
			position_text = doc.createTextNode ( str (item.cursorPosition) )
			position_tag.appendChild (position_text)

			# Команда TeX для кнопки
			image_tex_text = doc.createTextNode (item.tex_image)
			image_tex_tag.appendChild (image_tex_text)

			item_tag.appendChild (position_tag)
			item_tag.appendChild (text_tag)
			item_tag.appendChild (image_tag)
			item_tag.appendChild (image_tex_tag)

			group_tag.appendChild (item_tag)
		
		return group_tag
		


class ToolsPanel (wx.Notebook):
	def __init__ (self, parent):
		wx.Notebook.__init__(self, parent)
		self.SetAutoLayout (True)
		
	def AddPage (self, name, package_name):
		page = ToolsPage (self, name, package_name)
		wx.Notebook.AddPage (self, page, name)
		self.GetParent().Layout()		
		return page

	def onSize (self, event):
		self.GetParent().Layout()


class ToolsItem (object):
	def __init__ (self, parent, text, position, image, tex_image = ""):
		# Название инструмента
		self.text = text

		# Команда TeX для картинки на кнопке
		if len (tex_image) == 0:
			self.tex_image = text
		else:
			self.tex_image = tex_image
		
		# Картинка на кнопке
		self.image = image

		# Родительская панель, на которой должна лежать кнопка
		self.parent = parent
		
		self.id = wx.NewId()
		
		self.button = wx.BitmapButton (parent, self.id, \
			image.ConvertToBitmap(), \
			style = wx.BU_AUTODRAW )

		self.button.SetToolTipString (self.text)
		
		# Позиция курсора относительно начала вставляемого текста после вставки в редактор
		self.cursorPosition = position

		ToolsItem.makeMenu()

		self.button.Bind (wx.EVT_RIGHT_UP, self.onRightUp, id = self.id)
		self.button.Bind (wx.EVT_MENU, self.onRemove, id = ToolsItem.REMOVE_MENU)
		self.button.Bind (wx.EVT_MENU, self.onEdit, id = ToolsItem.EDIT_MENU)

	def destroy(self):
		self.button.Unbind (wx.EVT_RIGHT_UP, id = self.id)
		self.button.Unbind (wx.EVT_MENU, id = ToolsItem.REMOVE_MENU)
		self.button.Unbind (wx.EVT_MENU, id = ToolsItem.EDIT_MENU)
		
		self.button.GetParent().Unbind (wx.EVT_BUTTON, id = self.id)
		
		self.image.Destroy()
	
		# TODO: Разобраться почему при уничтожении кнопки из Linux (Gnome) происходит Secmentation Failed
		#self.button.Destroy()
		self.button.Hide()

	@staticmethod
	def makeMenu ():
		"""Создать меню для всех кнопок"""
		# Если меню уже создано, то ничего не делаем.
		if "REMOVE_MENU" not in dir (ToolsItem):
			ToolsItem.REMOVE_MENU = wx.NewId()
			ToolsItem.EDIT_MENU = wx.NewId()

			ToolsItem.popup_menu = wx.Menu ()
			ToolsItem.popup_menu.Append (ToolsItem.REMOVE_MENU, u"Удалить…")
			ToolsItem.popup_menu.Append (ToolsItem.EDIT_MENU, u"Редактировать…")

	def onRemove (self, event):
		result = wx.MessageBox (u"Удалить элемент", \
				u"Удаление элемента", wx.YES_NO | wx.ICON_QUESTION)

		if result == wx.YES:
			self.parent.RemoveItem (self)

	def onEdit (self, event):
		dlg = ToolsItemParamsDlg (self.parent, \
			text = self.text, \
			tex_image = self.tex_image, \
			position = self.cursorPosition)

		if dlg.ShowModal() == wx.ID_OK:
			self.text = dlg.GetItemTex()
			self.image = dlg.GetButtonImage()
			self.tex_image = dlg.GetButtonTex()
			self.cursorPosition = dlg.GetPosition()

			# Изменим картинку на кнопке
			self.button.SetBitmapLabel (self.image.ConvertToBitmap())

			# Ужмем кнопку до наименьшего размера
			self.button.SetSize (self.button.GetBestSize())

			# Выровняем размеры кнопок
			self.parent.SetToolsPositions()
			

	def onRightUp (self, event):
		self.button.PopupMenu (ToolsItem.popup_menu)


class ToolsPage (wx.NotebookPage ):
	"""Панель с кнопками для формул"""
	def __init__(self, parent, name, packagename):
		wx.NotebookPage.__init__(self, parent)
		
		self.SetAutoLayout (True)
		
		#self.package_dir = "tools"
		self.package_name = packagename
		#self.package_path = os.path.join (self.package_dir, packagename)
		
		self.emptyHeight = self.GetSize()[1]

		self.margin = 4
		
		# Название группы
		self.name = name
		
		# Кнопки-инструменты
		self.items = []
		
		self.texCompiler = tex.MimeTex ("mimetex")
		
		self.Bind (wx.EVT_SIZE, self.onSize)


	def onSize (self, event):
		self.SetToolsPositions()

	def AddItem (self, text, position, image, image_tex = ""):
		item = ToolsItem (self, text, position, image, image_tex)
		self.items.append (item)
		self.SetToolsPositions()
		return item

	def RemoveItem (self, item):
		#print self.items.index (item)
		item.destroy()
		self.items.remove (item)
		#item.button.Destroy()

	def GetItem (self, id):
		item = None
		for curr_item in self.items:
			#print curr_item.id
			if curr_item.id == id:
				item = curr_item
				break
				
		return item

	def GetItemsText (self):
		result = [item.text for item in self.items]
		return result

	def SetToolsPositions(self):
		xpos = self.margin
		ypos = self.margin
		lineheight = 0
		
		parent_size = self.GetParent().GetSize()
		
		maxheight = 0
		for item in self.items:
			button_size = item.button.GetSize()
			if button_size[1] > maxheight:
				maxheight = button_size[1]
		
		for item in self.items:
			button_size = item.button.GetSize()
			if xpos + button_size[0] > parent_size[0] - 8:
				xpos = self.margin
				ypos += self.margin + lineheight
				lineheight = 0
			
			item.button.SetSize ( (button_size[0], maxheight) )
			item.button.SetPosition ( (xpos, ypos) )
			xpos += button_size[0] + self.margin
			if button_size[1] > lineheight:
				lineheight = button_size[1]
		
		self.SetMinSize ( (-1, ypos + lineheight + self.margin * 2 ) )

		parentsize = self.GetParent().GetSize()[1]
		minparentsize = ypos + lineheight + self.margin * 2 + self.emptyHeight + 8
		
		if minparentsize > parentsize:
			self.GetParent().SetMinSize ( (-1, minparentsize) )
			
		self.GetParent().GetParent().Layout()
		self.GetParent().GetParent().Refresh()


class ToolsItemParamsDlg (wx.Dialog):
	"""Диалог для добавления нового элемента на панель"""
	def __init__(self, parent, text, tex_image = "", position = -1):
		wx.Dialog.__init__(self, parent)
		
		self.SetTitle (u"Добавить новый элемент")
		
		self.NAME_TEXT = wx.NewId()
		self.BUTTON_TEX = wx.NewId()
		self.POSITION_SPIN = wx.NewId()
		
		# Изображение на кнопку
		self.image = None
		
		self.texCompiler = tex.MimeTex ("mimetex")
		
		self.main_sizer = wx.FlexGridSizer(5, 2)
		self.main_sizer.AddGrowableCol (1)
		self.main_sizer.AddGrowableRow (4)
		
		# Элементы управления
		tex_label = wx.StaticText (self, -1, u"Команда TeX")
		self.tex_textbox = wx.TextCtrl (self, self.NAME_TEXT)

		position_label = wx.StaticText (self, -1, u"Позиция курсора")
		self.position_spin = wx.SpinCtrl (self, self.POSITION_SPIN)
		
		button_image_label = wx.StaticText (self, -1, u"Команда TeX для изображения на кнопке")
		self.button_tex_textbox = wx.TextCtrl (self, self.BUTTON_TEX)
	
		image_label = wx.StaticText (self, -1, u"Изображение нового элемента")
		self.item_image = wx.StaticBitmap (self)
		
		self.button_ok = wx.Button(self, wx.ID_OK, "OK")
		self.button_cancel = wx.Button(self, wx.ID_CANCEL, u"Отмена")
		
		
		self.Bind (wx.EVT_TEXT, self.onButtonTexEdit, self.button_tex_textbox)
		self.Bind (wx.EVT_TEXT, self.onItemTexEdit, self.tex_textbox)
		self.Bind (wx.EVT_BUTTON, self.onOk, id = wx.ID_OK)
		
		
		# Расстановка элементов управления
		# Каманда TeX
		self.main_sizer.Add (tex_label, flag = wx.ALIGN_CENTER_VERTICAL | wx.ALL, border = 4)
		self.main_sizer.Add (self.tex_textbox, \
			flag = wx.ALIGN_CENTER_VERTICAL | wx.ALL | wx.ALIGN_CENTER | wx.EXPAND, \
			border = 4)
		self.tex_textbox.SetValue (text)
		
		self.main_sizer.Add (position_label, flag = wx.ALIGN_CENTER_VERTICAL | wx.ALL, border = 4)
		self.main_sizer.Add (self.position_spin, \
			flag = wx.ALIGN_CENTER_VERTICAL | wx.ALL | wx.ALIGN_CENTER | wx.EXPAND, \
			border = 4)

		if position < 0:
			self.position_spin.SetValue ( len (text) )
		else:
			self.position_spin.SetValue (position)

		# команда TeX для картинке на кнопке
		self.main_sizer.Add (button_image_label, flag = wx.ALIGN_CENTER_VERTICAL | wx.ALL, border = 4)
		self.main_sizer.Add (self.button_tex_textbox, flag = wx.EXPAND | wx.ALIGN_CENTER_VERTICAL | wx.ALL | wx.ALIGN_CENTER, border = 4)

		if len (tex_image) == 0:
			self.button_tex_textbox.SetValue (text)
		else:
			self.button_tex_textbox.SetValue (tex_image)

		self.main_sizer.Add (image_label, flag = wx.ALIGN_CENTER_VERTICAL | wx.ALL, border = 4)
		self.main_sizer.Add (self.item_image, flag = wx.ALIGN_CENTER_VERTICAL | wx.ALL | wx.ALIGN_CENTER, border = 4)
		
		buttons_sizer = wx.StdDialogButtonSizer()
		buttons_sizer.AddButton (self.button_ok)
		buttons_sizer.AddButton (self.button_cancel)
		buttons_sizer.Realize ()
		
		self.main_sizer.AddSpacer(0)
		self.main_sizer.Add (buttons_sizer, flag = wx.ALIGN_BOTTOM | wx.ALIGN_RIGHT | wx.ALL, border = 4)
		
		self.UpdateElements()
		
		self.SetSizer (self.main_sizer)
		self.Fit()
		
	
	def onItemTexEdit (self, event):
		"""Редактирование команды TeX"""
		self.UpdateElements()
		self.position_spin.SetValue ( len (self.tex_textbox.GetValue()) )
		
	def onButtonTexEdit (self, event):
		self.UpdateElements()
	
	def onOk (self, event):
		text_item = self.tex_textbox.GetValue()
		if len (text_item) == 0:
			wx.MessageBox (u"Введите команду TeX для элемента",  u"Ошибка", wx.OK | wx.ICON_ERROR)
			return
		
		tex_button = self.button_tex_textbox.GetValue()
		if len (tex_button) == 0:
			wx.MessageBox (u"Введите команду TeX для изображения на кнопке",  u"Ошибка", wx.OK | wx.ICON_ERROR)
			return
		
		if not self.image:
			wx.MessageBox (u"Не создано изображение для кнопки",  u"Ошибка", wx.OK | wx.ICON_ERROR)
			return
		
		self.EndModal (wx.ID_OK)
		
	
	def UpdateElements (self):
		tex_button = self.button_tex_textbox.GetValue()
		self.image = self.texCompiler.compile (tex_button, True)
		
		tex_item = self.tex_textbox.GetValue()
		self.position_spin.SetRange (0, len (tex_item) )
		
		if self.image:
			self.item_image.SetBitmap (self.image.ConvertToBitmap() )
			self.Fit()
			self.Refresh()
	
	def GetItemTex(self):
		return self.tex_textbox.GetValue()
	
	def GetButtonTex (self):
		return self.button_tex_textbox.GetValue()
		
	def GetButtonImage (self):
		return self.image
		
	def GetPosition (self):
		return self.position_spin.GetValue()


class NewToolsGroupDlg (wx.Dialog):
	def __init__(self, parent, text, id = -1, title = "", pos = wx.DefaultPosition, \
			size = wx.DefaultSize, style = wx.DEFAULT_DIALOG_STYLE, name = "dialog"):
		wx.Dialog.__init__(self, parent, id, title, pos, size, style, name)
		
		self.SetTitle (u"Добавить новую группу")
		
		self.NAME_TEXT = wx.NewId()
		self.PACKAGE_TEXT = wx.NewId()
		
		self.texCompiler = tex.MimeTex ("mimetex")
		#image = self.texCompiler.compile (text)
		
		#self.main_sizer = wx.GridBagSizer()
		self.main_sizer = wx.FlexGridSizer(3, 2)
		self.main_sizer.AddGrowableCol (1)
		self.main_sizer.AddGrowableRow (2)
		
		#image_label = wx.StaticText (self, -1, u"Изображение для группы")
		#group_image = wx.StaticBitmap (self, -1, image.ConvertToBitmap())

		#self.main_sizer.Add (image_label, flag = wx.ALIGN_CENTER_VERTICAL | wx.ALL, border = 4)
		#self.main_sizer.Add (group_image, flag = wx.ALIGN_CENTER_VERTICAL | wx.ALL | wx.ALIGN_CENTER, border = 4)
		
		title_label = wx.StaticText (self, -1, u"Название группы")
		self.name_textbox = wx.TextCtrl (self, self.NAME_TEXT)
		
		self.main_sizer.Add (title_label, flag = wx.ALIGN_CENTER_VERTICAL | wx.ALL, border = 4)
		self.main_sizer.Add (self.name_textbox, \
			flag = wx.ALIGN_CENTER_VERTICAL | wx.ALL | wx.ALIGN_CENTER | wx.EXPAND, \
			border = 4)
		
		package_label = wx.StaticText (self, -1, u"Имя файла пакета (*.tools) без расширения")
		self.package_textbox = wx.TextCtrl (self, self.PACKAGE_TEXT)
		self.package_textbox.SetValue (u"users")
		
		self.main_sizer.Add (package_label, flag = wx.ALIGN_CENTER_VERTICAL | wx.ALL, border = 4)
		self.main_sizer.Add (self.package_textbox, \
			flag = wx.ALIGN_CENTER_VERTICAL | wx.ALL | wx.ALIGN_CENTER | wx.EXPAND, \
			border = 4)
			
		# OK / Cancel
		self.button_ok = wx.Button(self, wx.ID_OK, "OK")
		self.button_cancel = wx.Button(self, wx.ID_CANCEL, u"Отмена")
		
		buttons_sizer = wx.StdDialogButtonSizer()
		buttons_sizer.AddButton (self.button_ok)
		buttons_sizer.AddButton (self.button_cancel)
		buttons_sizer.Realize ()
		
		self.main_sizer.AddSpacer(0)
		self.main_sizer.Add (buttons_sizer, flag = wx.ALIGN_BOTTOM | wx.ALIGN_RIGHT | wx.ALL, border = 4)
		
		self.SetSizer (self.main_sizer)
		self.Fit()
	
	def GetGroupName(self):
		return self.name_textbox.GetValue()
	
	def GetPackageName (self):
		return self.package_textbox.GetValue()
